﻿using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Nxg.Blog.Core;
using Nxg.Blog.Core.Commands;
using Nxg.Blog.Core.Extensions;
using Nxg.Blog.Web.Features;

namespace Nxg.Blog.Web {
    public class Startup {
        public Startup (IConfiguration configuration) {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices (IServiceCollection services) {
            services.AddControllersWithViews (options => { });
            services.AddSingleton (typeof (IConfigProvider), ConfigProvider);
            AppConfiguration.SetupAppConfiguration (ConfigProvider);
            services.AddDbContext<DataContext> (options =>
                options.UseSqlite (Configuration.GetValue<string> ("ConnectionString"))

            ); //配置数据库连接字符串
            services.AddScoped<ISpamShieldService, SpamShieldService> ();

            services.AddSingleton<IMarkdownTransformer> (MarkdownTransformer);
            RegisterICommandInvoker (services);
            RegisterIViewProjections (services);
            services.AddAuthentication (CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie (CookieAuthenticationDefaults.AuthenticationScheme, options => {
                    options.LoginPath = new PathString ("/account/login/");
                    options.AccessDeniedPath = new PathString ("/account/forbidden/"); //禁止访问路径：当用户试图访问资源时，但未通过该资源的任何授权策略，请求将被重定向到这个相对路径。
                    options.Cookie.Name = "UserLoginCookie";
                    options.ReturnUrlParameter = "returnUrl";
                });
            services.AddTransient<IStartupFilter, StartupFilter> ();
        }

        public static void RegisterICommandInvoker (IServiceCollection services) {
            var commandInvokerTypes = typeof (ICommandInvoker<,>)
                .GetTypeInfo ()
                .Assembly
                .DefinedTypes
                .Select (t => new {
                    Type = t.AsType (),
                        Interface = t
                        .ImplementedInterfaces
                        .FirstOrDefault (i => i.GetTypeInfo ().IsGenericType && i.GetGenericTypeDefinition () == typeof (ICommandInvoker<,>))
                }).Where (t => t.Interface != null).ToArray ();

            foreach (var commandInvokerType in commandInvokerTypes) {
                services.AddScoped (commandInvokerType.Interface, commandInvokerType.Type);
            }
            services.AddScoped<ICommandInvokerFactory> (x => new CommandInvokerFactory (services.BuildServiceProvider ()));
        }

        public static void RegisterIViewProjections (IServiceCollection services) {
            var viewProjectionTypes = typeof (IViewProjection<,>)
                .GetTypeInfo ()
                .Assembly
                .DefinedTypes
                .Select (t => new {
                    Type = t.AsType (),
                        Interface = t
                        .ImplementedInterfaces
                        .FirstOrDefault (i => i.GetTypeInfo ().IsGenericType && i.GetGenericTypeDefinition () == typeof (IViewProjection<,>))
                }).Where (x => x.Interface != null).ToArray ();

            foreach (var viewProjectionType in viewProjectionTypes) {
                services.AddScoped (viewProjectionType.Interface, viewProjectionType.Type);
            }

            services.AddScoped<IViewProjectionFactory> (x => new ViewProjectionFactory (services.BuildServiceProvider ()));

            TagExtension.SetupViewProjectionFactory (services.BuildServiceProvider ().GetService<IViewProjectionFactory> ());

        }
        public void Configure (IApplicationBuilder app, IWebHostEnvironment env) {
            if (env.IsDevelopment ()) {
                app.UseDeveloperExceptionPage ();
            } else {
                app.UseExceptionHandler ("/home/error");
            }

            Console.WriteLine($"evn:{env.EnvironmentName}");

            app.UseStaticFiles ();

            app.UseRouting ();

            app.UseAuthorization ();

            app.UseAuthentication ();

            app.UseEndpoints (endpoints => {
                endpoints.MapControllerRoute (
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

        public IMarkdownTransformer MarkdownTransformer {
            get {
                return new MarkdownTransformer ();
            }
        }
        public ConfigProvider ConfigProvider {
            get {
                return new ConfigProvider (Configuration);
            }
        }
    }
}